/*-------------------------------------------------------------------
 * include 
 *-------------------------------------------------------------------*/
#include <iostream>
#include "socket.h"
#include "app/app_sock.h"

#ifdef __cplusplus
extern "C"
{
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>

#ifdef __cplusplus
}
#endif

/*-------------------------------------------------------------------
 * SocketSer 
 *-------------------------------------------------------------------*/
template<typename T>
SocketSer<T>::~SocketSer()
{
    if (sock_fd_ > 0)
    {
        close(sock_fd_);
        sock_fd_ = 0;
    }

    if (sock_app)
    {
        list_node *temp = sock_app;
        while(temp)
        {
            list_node *node = temp->next;
            delete[] temp->sock_app;
            delete[] temp;
            temp = node;
        }
        sock_app = nullptr;
        node_cnt = 0;
    }
}

/*-------------------------------------------------------------------
 * start 
 *-------------------------------------------------------------------*/
template<typename T>
void SocketSer<T>::start()
{
    while (true)
    {
        struct epoll_event recv_epoll[max_ + 1]{};
        int recv_cnt = epoll_wait(epol_fd_, recv_epoll, max_ + 1, -1);
        printf("epoll event cnt:%d\n", recv_cnt);
        for (int i = 0; i < recv_cnt && i < max_ + 1; i++)
        {
            int event_fd = recv_epoll[i].data.fd;
            if (sock_fd_ == event_fd)
            {
                struct sockaddr_in ipv4_addr{};
                unsigned int len{};
                char ip[128]{};
                socklen_t size{128};
                int client_fd = accept(sock_fd_, (struct sockaddr*)&ipv4_addr, &len);
                inet_ntop(AF_INET, &ipv4_addr.sin_addr.s_addr, ip, size);
                printf("client(%s, %d) fd:%d connect...\n", ip, ntohl(ipv4_addr.sin_port), client_fd);
                add_client(client_fd);

            }
            else
            {
                SocketApp *sock_app = get_client_sock(event_fd);
                if (recv_epoll[i].events | EPOLLIN)
                {
                    if (sock_app)
                    {
                        int size = read(event_fd, sock_app->get_buff_data(SocketApp::BUFF_R), BUFFER_SIZE);
                        if (0 == size)
                        {
                            printf("client fd:%d disconnect...\n", event_fd);
                            close(event_fd);
                            del_client(event_fd);
                        }
                        else
                        {
                            sock_app->set_buff_size(SocketApp::BUFF_R, size);
                            sock_app->read();
                        }
                    }
                }
                else if (recv_epoll[i].events | EPOLLOUT)
                {

                }
                else
                {}
            }
        }
    }

}

/*-------------------------------------------------------------------
 * init 
 *-------------------------------------------------------------------*/
template<typename T>
bool SocketSer<T>::init()
{
    bool ret = false;
    do 
    {
        if ((sock_fd_ = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            printf("socket failed:%s\n", strerror(errno));
            break;
        }
        int enable = 1;
        setsockopt(sock_fd_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));

        struct sockaddr_in ipv4_addr{};
        ipv4_addr.sin_family = AF_INET;
        ipv4_addr.sin_port   = ntohs(port_);
        inet_pton(AF_INET, ip_.data(), &ipv4_addr.sin_addr.s_addr);
        
        std::cout << "ip:" << ipv4_addr.sin_addr.s_addr << std::endl;

        if (bind(sock_fd_, (struct sockaddr*)&ipv4_addr, sizeof(struct sockaddr_in)) < 0)
        {
            printf("bind failed:%s\n", strerror(errno));
            break;
        }

        if (listen(sock_fd_, max_) < 0)
        {
            printf("listen failed:%s\n", strerror(errno));
            break;
        }

        epol_fd_ = epoll_create(1);

        struct epoll_event eve_type{};
        eve_type.events = EPOLLIN;
        eve_type.data.fd = sock_fd_;
        epoll_ctl(epol_fd_, EPOLL_CTL_ADD, sock_fd_, &eve_type);
        reactor_list_init();
        ret = true;
    } while(0);
    return ret;
}

/*-------------------------------------------------------------------
 * reactor_list_init 
 *-------------------------------------------------------------------*/
template<typename T>
bool SocketSer<T>::reactor_list_init()
{
    sock_app = new list_node;
    sock_app->sock_app = new T[NOTE_SOCK_MAX];
    sock_app->next = nullptr;
    node_cnt = 1;
    return true;
}

/*-------------------------------------------------------------------
 * resize_list 
 *-------------------------------------------------------------------*/
template<typename T>
bool SocketSer<T>::resize_list()
{
    list_node *node = new list_node;
    node->sock_app = new T[NOTE_SOCK_MAX];
    node->next = nullptr;
    list_node *temp{sock_app};
    while (temp && temp->next)
    {
        temp = temp->next;
    }
    temp->next = node;
    node_cnt++;
    return true;
}

/*-------------------------------------------------------------------
 * resize_list 
 *-------------------------------------------------------------------*/
template<typename T>
void SocketSer<T>::add_client(int fd)
{
    if (fd > 0 && sock_app)
    {
        int node_index = fd / NOTE_SOCK_MAX;
        while(node_index >= node_cnt)
        {
            printf("fd:%d node_index:%d cur_node_cnt:%d, resize_list.\n", fd, node_index, node_cnt);
            resize_list();
        }
        list_node *temp = sock_app;
        for (int i = 0; i < node_index; i++)
        {
            temp = temp->next;
        }
        temp->sock_app[fd % NOTE_SOCK_MAX].set_fd(fd);

        struct epoll_event eve_type{};
        eve_type.events = EPOLLIN;
        eve_type.data.fd = fd;
        epoll_ctl(epol_fd_, EPOLL_CTL_ADD, fd, &eve_type);
    }
}

/*-------------------------------------------------------------------
 * resize_list 
 *-------------------------------------------------------------------*/
template<typename T>
void SocketSer<T>::del_client(int fd)
{
    T* sock_app = get_client_sock(fd);
    if (sock_app)
    {
        sock_app->set_fd(-1);
        struct epoll_event eve_type{};
        eve_type.events = EPOLLIN;
        eve_type.data.fd = fd;
        epoll_ctl(epol_fd_, EPOLL_CTL_DEL, fd, &eve_type);
    }
}

/*-------------------------------------------------------------------
 * resize_list 
 *-------------------------------------------------------------------*/
template<typename T>
T* SocketSer<T>::get_client_sock(int fd)
{
    if (fd > 0 && sock_app)
    {
        int node_index = fd / NOTE_SOCK_MAX;
        if (node_index >= node_cnt)
        {
            return nullptr;
        }
        list_node *temp = sock_app;
        for (int i = 0; i < node_index; i++)
        {
            temp = temp->next;
        }
        return &temp->sock_app[fd % NOTE_SOCK_MAX];
    }
}